home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Deutsche Edition 1
/
Deutsche Edition 1.iso
/
amok
/
amok_lha
/
amok56.lha
/
TurboFiles_V2.0
/
(Turbo)Files.dok
< prev
next >
Wrap
Text File
|
1993-08-15
|
17KB
|
332 lines
DEFINITION (Turbo)Files V2.0;
CONST (* Der Modus für Open() *)
newFile = TRUE;
oldFile = FALSE;
CONST (* mögliche Werte von f.res *)
done* = 0; (* alles ok *)
notdone* = 1; (* unbekannter Fehler *)
notOpen* = 2; (* File nicht geoeffnet oder wieder geschlossen *)
openError = 3; (* Fehler beim Öffnen der Datei *)
readError = 4; (* Lesefehler, Diskette kaputt *)
writeError = 5; (* Schreibfehler, Diskette kaputt, Disk voll *)
seekError = 6; (* SetPos() ergab Position außerhalb der Datei *)
endOfFile = 7; (* Dateiende beim Lesen überschritten *)
outOfMem = 8; (* Zu wenig Speicher, Puffer bei Open() zu groß *)
notExists = 9; (* Datei existiert nicht *)
CONST (* Der Modus von SetPos() *)
beginning = Dos.beginning;
current = Dos.current;
end = Dos.end;
TYPE
File = RECORD
res : SHORTINT;
END;
PROCEDURE Open(VAR f: File; name: ARRAY OF CHAR;
bufferSize: LONGINT; new: BOOLEAN): BOOLEAN;
PROCEDURE ReadBytes (VAR f:File; adr: Exec.ADDRESS; len: LONGINT): LONGINT;
PROCEDURE WriteBytes(VAR f:File; adr: Exec.ADDRESS; len: LONGINT): BOOLEAN;
PROCEDURE ReadChar (VAR f: File; VAR ch: CHAR): BOOLEAN;
PROCEDURE WriteChar(VAR f: File; ch: CHAR): BOOLEAN;
PROCEDURE Read (VAR f: File; VAR block: ARRAY OF BYTE): BOOLEAN;
PROCEDURE Write(VAR f: File; block: ARRAY OF BYTE): BOOLEAN;
PROCEDURE ReadString (VAR f: File; VAR str: ARRAY OF CHAR): INTEGER;
PROCEDURE WriteString(VAR f: File; str: ARRAY OF CHAR): BOOLEAN;
PROCEDURE WriteLn(VAR f: File): BOOLEAN;
PROCEDURE Size(VAR f: File): LONGINT;
PROCEDURE GetPos(VAR f: File): LONGINT;
PROCEDURE SetPos(VAR f: File; offset: LONGINT; mode: LONGINT): BOOLEAN;
PROCEDURE Search(VAR f: File; str: ARRAY OF BYTE; len: INTEGER): LONGINT;
PROCEDURE Close(VAR f: File):BOOLEAN;
PROCEDURE Code(fileName, codeWord: ARRAY OF CHAR; decode: BOOLEAN):BOOLEAN;
PROCEDURE DeleteFile(name: ARRAY OF CHAR): BOOLEAN;
PROCEDURE Exists(name: ARRAY OF CHAR; VAR size: LONGINT): BOOLEAN;
END (Turbo)Files.
Dieses Modul vereinfacht und beschleunigt das Arbeiten mit Datein. Die Modu
le Files und TurboFiles sind vom Verhalten her identisch. In dem Modul Tur
boFiles sind allerdings einige Oberon-Prozeduren durch Assembleräquivalente
ersetzt worden. Dadurch ergibt sich eine merkliche Geschwindigkeits
steigerung, und der Programmcode wird fast 1 kByte kleiner. Ursprünglich war
Files bzw. TurboFiles als Ersatz für das FileSystem von M2Amiga gedacht. Ich
habe die Module nun nach Oberon übertragen und den Assemblerteil stark über
arbeitet. Der gesamte Assemblertext befindet sich jetzt in dem File Tur
boFiles.asm und kann mit dem FD-Assembler A68k assembliert werden. Man kann
das dadurch erzeugte File TurboFiles.o dann einfach mit dem CLI-Befehl JOIN
an TurboFiles.obj(s) anhängen und das Modul dann wie ein normales Oberon-
Modul benutzen. Die Assemblerprozeduren benutzen keine globalen statischen
Variablen und sind deshalb reentrant. Deshalb können Programme, die TurboFi
les benutzen und mit dem kleinen Datenmodell compiliert sind, weiterhin wie
gewohnt im CLI mit ARP.ARES oder RESIDENT installiert werden und auch
gleichzeitig mehrmals gestartet werden. Der Assemblerteil macht sich also in
keiner Weise negativ bemerkbar. Zudem beachten jetzt alle Assemblerprozedu
ren die Amiga-Dos Registerkonvention, d.h. es werden von den Prozeduren nur
die Register D0, D1, A0 und A1 verändert. Deshalb wird es auch mit zu
kümpftigen Compilerversionen keine Schwierigkeiten geben. Die Prozeduren
sind von der Namensgebung und dem Verhalten den Prozeduren des Oberon-
FileSystem sehr ähnlich, aus der Entwicklungsgeschichte und meinem
persönlichen Geschmack ergeben sich allerdings einige Unterschiede. So halte
ich den Namen ReadBytes() für treffender als ReadBlock(), und ich halte es
z.B. für sinnvoll, daß ReadBytes() statt eines Wertes vom Typ BOOLEAN lieber
die Anzahl der gelesenen Bytes zurückgibt.
Was ist nun der Vorteil von Files bzw. TurboFiles gegenüber dem Oberon-File
System? Zunächst einmal ist (Turbo)Files FD-Software, also frei kopierbar.
Es darf also auch von Personen benutzt werden, welche nur die Demoversion
des Oberoncompilers besitzen. Weiterhin hat (Turbo)Files einige Fähigkeiten,
die FileSystem nicht besitzt. So kann gleichzeitig aus einer Datei gelesen
und in sie geschrieben werden. (Diesem Modus unterstützt das Oberon-File-
System ab der Compilerversion 2.0 allerdings auch. ) Außerdem existieren
die Prozeduren Search() und Code() zum Durchsuchen und Codieren einer Datei.
Der Hauptvorteil ist aber die größere Geschwindigkeit von TurboFiles. Wärend
sich Files und FileSystem in der Geschwindigkeit kaum unterscheiden, ist
TurboFiles insbesondere beim Lesen und Schreiben von kleinen Datenblöcken
merklich schneller. So dauert das zeichenweise Lesen einer 100 kByte
großen Datei von RAM: mit FileSystem.ReadChar() ca. 8 Sekunden, mit
TurboFiles.ReadChar)_ aber nur 4 Sekunden. Beim Datenaustausch zur Diskette
ist der relative Geschwindigkeitsgewinn natürlich kleiner, da TurboFiles
nicht das langsame Diskettenlaufwerk beschleunigen kann. Ebenso bringt
TurboFiles nicht viel, wenn man große Datenblöcke, etwa Strings oder
Records, einliest oder in eine Datei schreibt. TurboFiles lohnt sich also
am meistem, wenn man Daten in sehr kleinen Portionen (Bytes, Integers ...)
von einem schnellen physikalischen Gerät (Ram-Disk, Festplatte) liest oder
dorthin schreibt. Da TurboFiles aber keinerlei Nachteile hat, sollte man es
am besten immer benutzen!
Hier zur Motivation ein Geschwindigkeitsvergleich von TurboFiles und dem
Oberon-FileSystem:
(Der Vergleich wurde mit dem beiligenden Programm SpeedCheck erzeugt. Es
wurde von RAM: eine 139 kByte große Datei gelesen bzw. eine gleich große
Datei erzeugt. TurboFiles benutzte wie FileSystem einen Puffer von 1024 kBy
te. Wie man sieht ist TurboFiles ca. doppelt so schnell wie FileSystem.)
TurboFiles vs. FileSystem
~~~~~~~~~~~~~~~~~~~~~~~~~~
Reading single Characters using ReadChar()
TurboFiles.ReadChar(): 5,58
FileSystem.ReadChar(): 13,56
Reading LONGINTs using ReadBytes()=ReadBlock()
TurboFiles.ReadBytes(): 3,18
FileSystem.ReadBlock(): 10,40
Reading LONGINTs using Read()
TurboFiles.Read(): 3,88
FileSystem.Read(): 9,76
Writing single Characters using WriteChar()
TurboFiles.WriteChar(): 8,46
FileSystem.WriteChar(): 16,90
Writing LONGINTSs using WriteBytes()=WriteBlock()
TurboFiles.WriteBytes(): 4,02
FileSystem.WriteBlock(): 11,02
Writing LONGINTSs using Write()
TurboFiles.Write(): 4,58
FileSystem.Write(): 12,36
Nun zu den Prozeduren des Moduls: Die Prozeduren, welche ein boolsches
Ergebnis liefern, waren immer erfolgreich, wenn sie TRUE zurückgeben. Ande
renfalls kann man das Feld f.res der File-Variablen abfragen, um genaueres
über die Fehlerursache zu erfahren. Das Feld f.res kann einen der folgenden
Werte annehmen:
CONST (* mögliche Werte von f.res *)
done* = 0; (* alles ok *)
notdone* = 1; (* unbekannter Fehler *)
notOpen* = 2; (* File nicht geoeffnet oder wieder geschlossen *)
openError = 3; (* Fehler beim Öffnen der Datei *)
readError = 4; (* Lesefehler, Diskette kaputt *)
writeError = 5; (* Schreibfehler, Diskette kaputt, Disk voll *)
seekError = 6; (* SetPos() ergab Position außerhalb der Datei *)
endOfFile = 7; (* Dateiende beim Lesen überschritten *)
outOfMem = 8; (* Zu wenig Speicher, Puffer bei Open() zu groß *)
notExists = 9; (* Datei existiert nicht *)
Solange kein Fehler aufgetreten ist, hat f.res den Wert done. Tritt ein Feh
ler auf, beispielsweise daß versucht wurde, über das Dateiende hinaus zu
lesen, so setzt die entsprechende Prozedur f.res auf einen Wert ungleich
done. In diesem Fall werden alle nachfolgenden Prozeduraufrufe außer Close()
ignoriert.
PROCEDURE Open(VAR f: File; name: ARRAY OF CHAR;
bufferSize: LONGINT; new: BOOLEAN): BOOLEAN;
Bevor man die folgenden Prozeduren zur Bearbeitung einer Datei benutzen
kann, muß diese durch Open() geöffnet werden. Open() öffnet die Dos-Datei
und initialisiert die File-Variable. name ist der Name der Datei und muß ein
gültiger Amiga-Dos Filename sein, wie etwas "Ram:Text1" oder "PRT:" . Durch
bufferSize wird angegeben, wie groß der beim Lesen und Schreiben benutzte
Puffer sein soll. In der Regel ist 1 kByte ausreichend, beim Schreiben auf
Diskette ist allerdings ein größerer Puffer von beispielsweise 10 kByte
vorteilhaft. Ist new=TRUE, so wird eine neue Datei angelegt, ist new=FALSE
wird versucht, eine existierende Datei zu öffnen.
Die Prozeduren zum Lesen von Daten:
PROCEDURE Read(VAR f: File; VAR block: ARRAY OF BYTE): BOOLEAN;
Liest LEN(block) Bytes aus der Datei und speichert sie in block. block ist
kompatibel zu allen Datentypen, man kann mit Read() also sowohl Records als
auch INTEGER- oder REAL-Zahlen einlesen.
PROCEDURE ReadChar(VAR f: File; VAR ch: BYTE): BOOLEAN;
Liest ein einzelnes Zeichen bzw. BYTE aus der Datei. Statt ReadChar() kann
man auch Read() verwenden, TurboFiles.ReadChar() ist aber wesentlich schnel
ler.
PROCEDURE ReadString(VAR f: File; VAR str: ARRAY OF CHAR): INTEGER;
Liest mit Hilfe von ReadChar() eine Zeichenkette aus der Datei. Als String
ende wird ein 0X oder ein LineFeed (0AX) angesehen. Maximal werden jedoch
LEN(str) Zeichen gelesen. Als Resultat wird die Länge des gelesenen Strings
zurückgegeben.
PROCEDURE ReadBytes(VAR f: File; adr: Exec.ADDRESS; len: LONGINT): LONGINT;
ReadBytes() ist (da der Datentyp Exec.ADDRESS benutzt wird) eine sogenannte
Low-Level-Prozedur und sollte deshalb nur benutzt werden, wenn dies unum
gänglich ist, wenn also z.B. Daten an eine ganz bestimmte Adresse geladen
werden müssen. adr gibt an, wohin die Daten geladen werden sollen, und len
gibt an, wie viele Bytes gelesen werden sollen. Als Resultat erhält man die
Anzahl Bytes, die gelesen werden konnten. Trat kein Fehler auf, so ist die
ses Resultat gleich len; wurde aber z.B. versucht über das Dateiende hinaus
zu lesen, so ist das Resultat kleiner als len und f.res=endOfFile.
Die Prozeduren zum Schreiben von Daten in eine Datei:
PROCEDURE Write(VAR f: File; block: ARRAY OF BYTE): BOOLEAN;
Schreibt LEN(block) Bytes in die Datei. Dem Parameter block können beliebi
ge Datentypen zugewiesen werden, also beispielsweise Records oder auch LON
GREAL-Zahlen.
PROCEDURE WriteChar(VAR f: File; ch: BYTE): BOOLEAN;
Schreibt ein einzelnes Zeichen bzw. Byte in die Datei. Man kann auch Write()
benutzen, TurboFiles.WriteChar() ist aber deutlich schneller.
PROCEDURE WriteString(VAR f: File; str: ARRAY OF CHAR): BOOLEAN;
Schreibt die Zeichenkette str in die Datei. Die Zeichenkette wird NICHT
durch ein LineFeed abgeschlossen. Soll der nächste String in eine neue Zeile
geschrieben werden, so muß man vorher WriteChar(f,ASCII.lf) oder WriteLn(f)
aufrufen.
PROCEDURE WriteBytes(VAR f: File; adr: Exec.ADDRESS; len: LONGINT): BOOLEAN;
Dies ist wiederum eine Low-Level Prozedur. Sie sollte nur benutzt werden,
wenn die anderen Schreibprozeduren nicht einsetzbar sind. adr gibt an, wo
die Daten stehen, welche in die Datei geschrieben werden sollen, und len
gibt an, wie viele Bytes geschrieben werden sollen.
Nun zu den Prozeduren GetPos, SetPos und Size:
PROCEDURE GetPos(VAR f: File): LONGINT;
GetPos() liefert die momentane Position in der Datei, gemessen vom Dateian
fang. Sollte bei vorherigen Dateioperationen bereits ein Fehler aufgetreten
sein, und deshalb f.res einen Wert ungleich done haben, so ergibt GetPos()
als Resultat den Wert -1.
PROCEDURE SetPos(VAR f: File; offset: LONGINT; mode: LONGINT): BOOLEAN;
Mit dieser Prozedur kann man an eine beliebige Stelle innerhalb einer Datei
springen. offset bestimmt die neue Position. Ist mode=beginning, so wird
offset vom Anfang der Datei aus gemessen, ist mode=end, so vom Ende der
Datei aus. Ist mode=current, so wird die aktuelle Position um offset ver
schoben; dabei verschiebt ein positiver Wert die Position zum Dateiende, ein
negativer zum Dateianfang. Wird versucht, eine Position außerhalb der Datei
grenzen anzuwählen, so wird f.res auf seekError gesetzt und FALSE zurückge
geben.
PROCEDURE Size(VAr f: File): LONGINT;
Size() liefert die momentane Dateigröße in Bytes. Hat f.res einen Wert
ungleich done, so wird -1 zurückgegeben.
PROCEDURE Search(VAR f: File; str: ARRAY OF BYTE; len: INTEGER): LONGINT;
Sucht in der Datei ab der momentanen Position nach str, wobei nur die ersten
len Bytes von str berücksichtigt werden. Wird str gefunden, so wird die ak
tuelle Position auf diese Stelle gesetzt und diese Position wird zurückgege
ben. Wird str nicht gefunden, so wird -1 zurückgegeben und f.res hat einen
Wert ungleich done.
PROCEDURE Close(VAR f: File):BOOLEAN;
Schließt eine offene Datei. Sobald man mit einer Datei nicht mehr arbeiten
will, sollte man sie mit Close() schliessen. Wird das Programm durch einen
Laufzeitfehler oder Control-C abgebrochen, so wird die Dos-Datei automatisch
geschlossen, allerdings wird vorher der eventuell durch Schreiboperationen
veränderte Puffer nicht in die Dos-Datei abgespeichert. Ist die Datei
bereits geschlossen, oder war das Öffnen nicht erfolgreich, so gibt Close()
den Wert FALSE zurück, ohne irgendetwas zu tun. Das Gleiche tut Close(),
wenn f.res den Wert notOpen hat. Nachdem die File-Variable durch Open()
initialisiert worden ist, kann man jederzeit Close() aufrufen, egal ob
Open() erfolgreich war oder nicht. Setzt man f.res am Beginn des Programms
auf den Wert notOpen, so ist sichergestellt, daß ein (durch einen Programm-
abbruch verursachtes) Close() nicht mit einer uninitiallisierten File-
Variablen arbeitet.
Die folgenden drei Prozeduren dürfen nur auf geschlossene bzw. gar nicht
erst geöffnete Dateien angewendet werden:
PROCEDURE DeleteFile(name: ARRAY OF CHAR): BOOLEAN;
Löscht eine Datei. Synonym für Dos.Delete().
PROCEDURE Exists(name: ARRAY OF CHAR; VAR size: LONGINT): BOOLEAN;
Exists() überprüft, ob eine Datei bzw. ein Verzeichnis existiert. Existiert
die Datei bzw. das Directory, wird TRUE zurückgegeben,anderenfalls FALSE.
Handelt es sich um eine Datei, enthält size deren Größe in Bytes. Ist es ein
Verzeichnis, wird size auf den Wert -1 gesetzt.
PROCEDURE Code(fileName, codeWord: ARRAY OF CHAR; decode: BOOLEAN):BOOLEAN;
Diese Prozedur dient zum Codieren von beliebigen Files. codeWord ist ein
beliebiger String, der alle Zeichen bis auf 0X enthalten darf und nicht län
ger als 126 Zeichen sein sollte. decode gibt an ob die Datei codiert oder
decodiert werden soll. Man kann jede beliebige Datei codieren. Danach ist
sie solange unlesbar und unbrauchbar, bis sie wieder mit dem gleichen Code
wort decodiert worden ist. Die codierte Datei hat die selbe Größe und den
selben Namen wie das Original, d.h. das Original wird überschrieben!
Zum Schluß noch einige allgemeine Bemerkungen:
Wie schon gesagt, werden alle Prozeduraufrufe außer Close() ignoriert,
wenn f.res einen Wert ungleich done hat. In der Regel ist dann ein Fehler
aufgetreten und man wird die Datei schließen. In einigen Fällen kann es aber
sinnvoll sein, die Datei weiter zu bearbeiten, beispielsweise wenn durch
Search() oder Read() das Dateiende erreicht worden ist. Man kann dann f.res
auf done zurücksetzen und versuchen die Datei weiter zu bearbeiten.
Das optimierende Linken des Assemblerteils von TurboFiles funktioniert mom
entan nicht. Der Assemblerteil besteht aus 9 Prozeduren, welche sich alle in
einem File befinden. Mit dem FD-Assembler A68k ist es mir nicht gelungen,
für jede Prozedur einen separaten Unit-HUNK zu erzeugen. Die neun Prozeduren
befinden sich daher alle in einem einzigen HUNK. Wird nun eine dieser neun
Prozeduren benutzt, so linkt BLink den ganzen Hunk mit allen neun Prozedu
ren zum Hauptprogramm dazu. Das ist zwar nicht weiter tragisch, da die neun
Prozeduren zusammen nur ca. 1 kByte groß sind, aber es ist doch ein kleiner
Schönheitsfehler.
TurboFiles V2.0 wurde auf einen A1000 mit KS und WB 1.3 entwickelt und ge-
testet, sollte aber auf jeden Amiga laufen.
Stefan Salewski, 9. Juni 1991